import { Layer } from "./layer";
import { requestAnimFrame } from "/tool";
import { AnimationFn, IFrame } from "/types/type";
const now = (function (): () => number {
	if (performance && performance.now) {
		return function () {
			return performance.now();
		};
	}

	return function () {
		return new Date().getTime();
	};
})();
export class AnimationLayer {
	func: AnimationFn;
	id = AnimationLayer.animIdCounter++;

	layers: Layer[];

	frame: IFrame = {
		time: 0,
		timeDiff: 0,
		lastTime: now(),
		frameRate: 0,
	};

	constructor(func: AnimationFn, layers?: Layer | Layer[]) {
		this.func = func;
		this.setLayers(layers);
	}
	/**
	 * set layers to be redrawn on each animation frame
	 * @method
	 * @name Konva.Animation#setLayers
	 * @param {Konva.Layer|Array} [layers] layer(s) to be redrawn. Can be a layer, an array of layers, or null.  Not specifying a node will result in no redraw.
	 * @return {Konva.Animation} this
	 */
	setLayers(layers: null | Layer | Layer[]) {
		let lays: Layer[] = [];
		// if passing in no layers
		if (layers) {
			lays = Array.isArray(layers) ? layers : [layers];
		}
		this.layers = lays;
		return this;
	}
	/**
	 * get layers
	 * @method
	 * @name Konva.Animation#getLayers
	 * @return {Array} Array of Konva.Layer
	 */
	getLayers() {
		return this.layers;
	}
	/**
	 * add layer.  Returns true if the layer was added, and false if it was not
	 * @method
	 * @name Konva.Animation#addLayer
	 * @param {Konva.Layer} layer to add
	 * @return {Bool} true if layer is added to animation, otherwise false
	 */
	addLayer(layer: Layer) {
		const layers = this.layers;
		const len = layers.length;

		// don't add the layer if it already exists
		for (let n = 0; n < len; n++) {
			if (layers[n]._id === layer._id) {
				return false;
			}
		}

		this.layers.push(layer);
		return true;
	}
	/**
	 * determine if animation is running or not.  returns true or false 判断动画是否还在运行
	 * @method
	 * @name Konva.Animation#isRunning
	 * @return {Bool} is animation running?
	 */
	isRunning() {
		const a = AnimationLayer;
		const animations = a.animations;
		const len = animations.length;

		for (let n = 0; n < len; n++) {
			if (animations[n].id === this.id) {
				return true;
			}
		}
		return false;
	}
	/**
	 * start animation
	 * @method
	 * @name Konva.Animation#start
	 * @return {Konva.Animation} this
	 */
	start() {
		this.stop();
		this.frame.timeDiff = 0;
		this.frame.lastTime = now();
		AnimationLayer._addAnimation(this);
		return this;
	}
	/**
	 * stop animation
	 * @method
	 * @name Konva.Animation#stop
	 * @return {Konva.Animation} this
	 */
	stop() {
		AnimationLayer._removeAnimation(this);
		return this;
	}
	_updateFrameObject(time: number) {
		this.frame.timeDiff = time - this.frame.lastTime;
		this.frame.lastTime = time;
		this.frame.time += this.frame.timeDiff;
		this.frame.frameRate = 1000 / this.frame.timeDiff;
	}

	static animations:AnimationLayer[] = [];
	static animIdCounter = 0;
	static animRunning = false;

	static _addAnimation(anim:AnimationLayer) {
		this.animations.push(anim);
		this._handleAnimation();
	}
	static _removeAnimation(anim:AnimationLayer) {
		const id = anim.id;
		const animations = this.animations;
		const len = animations.length;

		for (let n = 0; n < len; n++) {
			if (animations[n].id === id) {
				this.animations.splice(n, 1);
				break;
			}
		}
	}

	static _runFrames() {
		const layerHash:{[index:string]:Layer} = {};
		const animations = this.animations;
		/*
		 * loop through all animations and execute animation
		 *  function.  if the animation object has specified node,
		 *  we can add the node to the nodes hash to eliminate
		 *  drawing the same node multiple times.  The node property
		 *  can be the stage itself or a layer
		 */
		/*
		 * WARNING: don't cache animations.length because it could change while
		 * the for loop is running, causing a JS error
		 */

		for (let n = 0; n < animations.length; n++) {
			const anim = animations[n];
			const layers = anim.layers;
			const func = anim.func;

			anim._updateFrameObject(now());
			const layersLen = layers.length;

			// if animation object has a function, execute it
			let needRedraw;
			if (func) {
				// allow anim bypassing drawing
				needRedraw = func.call(anim, anim.frame) !== false;
			} else {
				needRedraw = true;
			}
			if (!needRedraw) {
				continue;
			}
			for (let i = 0; i < layersLen; i++) {
				const layer = layers[i];

				if (layer._id !== undefined) {
					layerHash[layer._id] = layer;
				}
			}
		}

		for (let key in layerHash) {
			if (!layerHash.hasOwnProperty(key)) {
				continue;
			}
			layerHash[key].refresh();
		}
	}
	static _animationLoop() {
		const Anim = AnimationLayer;
		if (Anim.animations.length) {
			Anim._runFrames();
			requestAnimFrame(Anim._animationLoop);
		} else {
			Anim.animRunning = false;
		}
	}
	static _handleAnimation() {
		if (!this.animRunning) {
			this.animRunning = true;
			requestAnimFrame(this._animationLoop);
		}
	}
}
